package com.bruce.asurada.sso.service.impl;

import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.bruce.asurada.sso.bean.Client;
import com.bruce.asurada.common.Result;
import com.bruce.asurada.sso.config.SsoConfig;
import com.bruce.asurada.sso.constants.TokenConstants;
import com.bruce.asurada.common.dto.PhoneCodeLoginDTO;
import com.bruce.asurada.common.dto.TokenDTO;
import com.bruce.asurada.common.dto.UserInfoDto;
import com.bruce.asurada.common.dto.UserLoginDto;
import com.bruce.asurada.common.dto.UserQueryDTO;
import com.bruce.asurada.sso.feign.UserService;
import com.bruce.asurada.sso.service.PhoneCodeService;
import com.bruce.asurada.sso.service.SsoService;
import com.bruce.asurada.sso.util.JwtUtil;
import com.bruce.asurada.sso.util.RedisUtil;
import com.bruce.asurada.sso.util.ServiceCallUtil;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class SsoServiceImpl implements SsoService {

    

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private UserService userService ;

    @Autowired
    private PhoneCodeService phoneCodeService;

    @Autowired
    private SsoConfig ssoConfig ;

    @Autowired
    private ServiceCallUtil serviceCallUtil;

    /**
     * 生成用户授权码
     * @param loginDto
     * @return
     * @throws Exception
     */
    @Override
    public String generateAuthCode(UserLoginDto loginDto) throws Exception {
        // 1.验证客户端clientId
        String clientId = loginDto.getClientId();
        if (!isValidClient(clientId)) {
            throw new Exception("无效的客户端");
        }
        // 2.获取用户信息
        UserInfoDto userInfoDto =  getLoginUserInfo(loginDto);
        if(StringUtils.isEmpty(clientId)){
            throw new RuntimeException("客户端ID不能为空");
        }
        Long userId = userInfoDto.getId() ;
        String username = userInfoDto.getUsername();
        String authCode = UUID.randomUUID().toString().replace("-", "");
        redisUtil.set(TokenConstants.AUTO_CODE_PREFIX + ":"+authCode, userId + ":" + username + ":"+clientId,5,TimeUnit.MINUTES);
        return authCode;
    }

     private boolean isValidClient(String clientId) {
        List<Client> clients = ssoConfig.getClients();
        if(clients == null || clients.isEmpty()){
            log.error("客户端列表为空");
            return false;
        }
        
        for(Client client : clients){
            if(client.getClientId().equals(clientId)){
                return true;
            }
        }
        return false;
    }

    /**
     * 获取登录用户信息
     * @param loginDto
     * @return
     */
    private UserInfoDto getLoginUserInfo(UserLoginDto loginDto) {
        Result<UserInfoDto> userResut = userService.login(loginDto);
        if(null == userResut || null == userResut.getData()){
            throw new RuntimeException("用户信息不能为空");
        }
        return userResut.getData();
    }

    /**
     * 生成token
     * @param userInfoDto
     * @return
     */
    @Override
    public TokenDTO generateTokens(UserLoginDto loginDto) throws Exception{ 

        String clientId = loginDto.getClientId();
        if (!isValidClient(clientId)) {
            throw new RuntimeException("无效的客户端");
        }

        String authCode = loginDto.getAuthCode() ;
        if(StringUtils.isEmpty(authCode)){
            throw new RuntimeException("用户授权码不能为空");
        }
        Object authCodeObject = redisUtil.get(TokenConstants.AUTO_CODE_PREFIX + ":"+authCode);
        if(authCodeObject == null){
            throw new RuntimeException("用户授权码无效或者已过期");
        }
        String objson = (String) authCodeObject;
        String[] split = objson.split(":");
        if(split.length < 2){
            throw new RuntimeException("用户授权码格式错误");
        }
        Long storeUserId = Long.valueOf(split[0]);
        String storeUserName = split[1];
        String storeClientId = split[2];

        if(!loginDto.getClientId().equals(storeClientId)){
            throw new RuntimeException("授权码与客户端不匹配");
        }

        UserInfoDto userInfoDto =  getLoginUserInfo(loginDto);
        if(!storeUserId.equals(userInfoDto.getId()) || !storeUserName.equals(userInfoDto.getUsername())){
            log.error("授权码与当前登录用户不匹配: 授权码用户:Id{}，用户名{},当前登录用户:{}", storeUserId,storeUserName,userInfoDto);
            throw new RuntimeException("授权码与当前登录用户不匹配");
        }


        // 删掉已使用的授权码
        redisUtil.delete(TokenConstants.AUTO_CODE_PREFIX + ":"+authCode);

        // 生成令牌
        return generateTokenDTO(storeUserName,storeUserId) ;
    }


    /**
     * 用手机号+短信验证码登录
     */
    @Override
    public TokenDTO loginByPhoneAndCode(PhoneCodeLoginDTO loginDto)throws Exception{
        String mobile = loginDto.getPhone();
        String authCode = loginDto.getAuthCode();
        UserQueryDTO queryDTO = UserQueryDTO.builder().mobile(mobile).build();
        Result<UserInfoDto> result = serviceCallUtil.executeWithRetry(
            "asurada-user", 
            "getUserInfo", 
            () -> userService.getUserInfo(queryDTO), 
            2  // 最多重试2次
        );
        if (null == result || null == result.getData()){
            throw new RuntimeException("手机号输入错误！");
        }

        UserInfoDto user = result.getData();
        if (!phoneCodeService.verifyCode(mobile, loginDto.getPhoneCode())){
            throw new RuntimeException("验证码输入错误！");
        }

        if(StringUtils.isEmpty(authCode)){
            throw new RuntimeException("用户授权码不能为空");
        }

        Object authCodeObject = redisUtil.get(TokenConstants.AUTO_CODE_PREFIX + ":"+authCode);
        if(authCodeObject == null){
            throw new RuntimeException("用户授权码无效或者已过期");
        }

        String objson = (String) authCodeObject;
        String[] split = objson.split(":");
        String storeClientId = split[2];
        if(split.length < 2){
            throw new RuntimeException("用户授权码格式错误");
        }
       if(!loginDto.getClientId().equals(storeClientId)){
            throw new RuntimeException("授权码与客户端不匹配");
        }

        // 删掉已使用的授权码
        redisUtil.delete(TokenConstants.AUTO_CODE_PREFIX + ":"+authCode);

        // 生成令牌
        return generateTokenDTO(user.getUsername(),user.getId()) ;
    }


    private TokenDTO generateTokenDTO(String username, Long userId) throws Exception{
// 生成令牌
        String accessToken = jwtUtil.generateAccessToken(username,userId);
        String imSessionToken = jwtUtil.generateImSessionToken(username,userId);
        String imAccessToken = jwtUtil.generateImAccessToken(username,userId);
        String refreshToken = jwtUtil.generateRefreshToken(username);

        // 将令牌存储到Redis
        Long expirationMs = jwtUtil.getExpirationInSeconds() * 1000;
        redisUtil.set(TokenConstants.TOKEN_PREFIX + accessToken, userId, expirationMs, TimeUnit.MILLISECONDS);
        redisUtil.set(TokenConstants.IM_ACCESS_TOKEN_PREFIX + imAccessToken, userId, expirationMs, TimeUnit.MILLISECONDS);
        redisUtil.set(TokenConstants.IM_SESSION_TOKEN_PREFIX + imSessionToken, userId, expirationMs, TimeUnit.MILLISECONDS);
        redisUtil.set(TokenConstants.REFRESH_TOKEN_PREFIX + refreshToken, userId, 7, TimeUnit.DAYS);


        

        TokenDTO tokenDTO = TokenDTO.builder()
                .accessToken(accessToken)
                .refreshToken(refreshToken)
                .imSessionToken(imSessionToken)
                .imAccessToken(imAccessToken)
                .build();
        return tokenDTO ;
    }

    /**
     * 检查登录状态
     * @param token
     * @return
     * @throws Exception
     */
    public boolean checkToken(String token) throws Exception{
       return redisUtil.hasKey(TokenConstants.TOKEN_PREFIX + token) && !jwtUtil.isTokenExpired(token);
    }

    /**
     * 验证token
     */
     @Override
    public UserInfoDto validateToken(String token) throws Exception {
       // 检查Redis中是否存在该令牌
       boolean isValid = redisUtil.hasKey(TokenConstants.TOKEN_PREFIX + token) && !jwtUtil.isTokenExpired(token);
       if (!isValid) {
            throw new Exception("令牌无效或者已过期");
       }
       return generateUserInfoFromToken(token) ;
    }

    /**
     * 验证IM会话令牌
     */
    public UserInfoDto validateImSessionToken(String imSessionToken)throws Exception{
        // 检查Redis中是否存在该令牌
       boolean isValid = redisUtil.hasKey(TokenConstants.IM_SESSION_TOKEN_PREFIX + imSessionToken) && !jwtUtil.isTokenExpired(imSessionToken);
       if (!isValid) {
            throw new Exception("IM令牌无效或者已过期");
       }
       return generateUserInfoFromToken(imSessionToken) ;
    }

    private UserInfoDto generateUserInfoFromToken(String token)throws Exception{
      Long userId = jwtUtil.getUserIdFromToken(token);
       UserQueryDTO queryDTO = UserQueryDTO.builder().id(userId).build();
       Result<UserInfoDto> result = serviceCallUtil.executeWithRetry(
           "asurada-user", 
           "getUserInfo", 
           () -> userService.getUserInfo(queryDTO), 
           1  // 验证token时只重试1次
       );
       log.debug("令牌验证成功，用户ID: {}", userId);
       return result.getData();
    }

    @Override
    public TokenDTO refreshToken(String refreshToken) throws Exception{
        try {
            // 验证刷新令牌
            String username = jwtUtil.getUsernameFromToken(refreshToken);
            
            // 检查Redis中是否存在该刷新令牌
            Object userId = redisUtil.get(TokenConstants.REFRESH_TOKEN_PREFIX + refreshToken);
            if (userId == null) {
                throw new RuntimeException("刷新令牌已过期");
            }

            UserQueryDTO queryDTO = UserQueryDTO.builder()
                    .username(username)
                    .build();
            Result<UserInfoDto> userResult = serviceCallUtil.executeWithRetry(
                "asurada-user", 
                "getUserInfo", 
                () -> userService.getUserInfo(queryDTO), 
                1  // 刷新token时只重试1次
            );
            if (null == userResult || null == userResult.getData()) {
                throw new RuntimeException("用户不存在");
            }
            UserInfoDto userInfo = userResult.getData();
            // 生成新的访问令牌
            String newAccessToken = jwtUtil.generateAccessToken(userInfo.getUsername(), userInfo.getId());
            String newRefreshToken = jwtUtil.generateRefreshToken(userInfo.getUsername());

            // 删除旧的刷新令牌，存储新的令牌
            redisUtil.delete(TokenConstants.REFRESH_TOKEN_PREFIX + refreshToken);
            Long expirationMs = jwtUtil.getExpirationInSeconds() * 1000;
            redisUtil.set(TokenConstants.TOKEN_PREFIX + newAccessToken, userInfo.getId(), expirationMs, TimeUnit.MILLISECONDS);
            redisUtil.set(TokenConstants.REFRESH_TOKEN_PREFIX + newRefreshToken, userInfo.getId(), 7, TimeUnit.DAYS);

            TokenDTO tokenDTO = TokenDTO.builder()
                    .accessToken(newAccessToken)
                    .refreshToken(newRefreshToken)
                    .tokenType("Bearer")
                    .expiresIn(jwtUtil.getExpirationInSeconds())
                                .build() ;
            return tokenDTO;                    

        } catch (Exception e) {
            throw new RuntimeException("刷新令牌失败: " + e.getMessage());
        }
    }


    @Override
    public void logout(TokenDTO tokenDTO) throws Exception{
        // 从Redis中删除令牌
        String token = tokenDTO.getAccessToken();
        String username = "";
        if(StringUtils.isNotBlank(token)){
            username = jwtUtil.getUsernameFromToken(token);
            redisUtil.delete(TokenConstants.TOKEN_PREFIX + token);
        }
        String refreshToken = tokenDTO.getRefreshToken();
        if(StringUtils.isNotBlank(refreshToken)){
            redisUtil.delete(TokenConstants.REFRESH_TOKEN_PREFIX + refreshToken);
        }
        String imSessionToken = tokenDTO.getImSessionToken();
        if(StringUtils.isNotBlank(imSessionToken)){
            redisUtil.delete(TokenConstants.IM_SESSION_TOKEN_PREFIX + imSessionToken);
        }
        String imAccessToken = tokenDTO.getImAccessToken();
        if(StringUtils.isNotBlank(imAccessToken)){
            redisUtil.delete(TokenConstants.IM_ACCESS_TOKEN_PREFIX + imAccessToken);
        }
        log.info("用户{}登出成功", username);
    }

}
